package com.suning.sawp.service.impl.promotion;

import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import com.google.gson.reflect.TypeToken;
import com.suning.rsc.RscException;
import com.suning.sawp.constants.CacheKeyConstants;
import com.suning.sawp.constants.ConfigConstants;
import com.suning.sawp.constants.ErrorCodeConstants;
import com.suning.sawp.constants.LoggerConstants;
import com.suning.sawp.constants.PromSysConstants;
import com.suning.sawp.constants.PromotionConstants;
import com.suning.sawp.constants.PromotionErrorConstants;
import com.suning.sawp.dto.common.QueryResult;
import com.suning.sawp.dto.common.ReturnMsg;
import com.suning.sawp.dto.customer.CustshopFrequency;
import com.suning.sawp.dto.esb.LocateMemberInfo;
import com.suning.sawp.dto.esb.QryActDetailRsp;
import com.suning.sawp.dto.esb.QryActDetailRsp.ActivityDetail;
import com.suning.sawp.dto.esb.QryPromExecDetailReq;
import com.suning.sawp.dto.esb.QryPromExecDetailRsp;
import com.suning.sawp.dto.esb.SendCouponRtn;
import com.suning.sawp.dto.promotion.HorizProActivityShowDto;
import com.suning.sawp.dto.promotion.HorizProCouponParam;
import com.suning.sawp.dto.promotion.HorizProRecordShowDto;
import com.suning.sawp.dto.promotion.HorizProRtn;
import com.suning.sawp.dto.promotion.QueryHorActBean;
import com.suning.sawp.dto.promotion.QueryHorActBean.OperStatus;
import com.suning.sawp.dto.promotion.StatisticsHorCoupon;
import com.suning.sawp.dto.promotion.ValidHorizProCouponRecord;
import com.suning.sawp.exception.MobileException;
import com.suning.sawp.intf.dao.HorizPromotionDaoService;
import com.suning.sawp.intf.esb.MemberInfoService;
import com.suning.sawp.intf.esb.PromotionaMgmtService;
import com.suning.sawp.intf.esb.ShopFrequencyService;
import com.suning.sawp.intf.precharge.SequenceSerivce;
import com.suning.sawp.intf.promotion.HorizPromotionService;
import com.suning.sawp.intf.promotion.PromotionCouponService;
import com.suning.sawp.intf.store.StoreInfoService;
import com.suning.sawp.po.promotion.HorizProActivity;
import com.suning.sawp.po.promotion.HorizProCoupon;
import com.suning.sawp.po.promotion.HorizProRecord;
import com.suning.sawp.po.store.StoreInfo;
import com.suning.sawp.service.util.AESUtils;
import com.suning.sawp.service.util.ConfigUtil;
import com.suning.sawp.service.util.DJStringUtils;
import com.suning.sawp.service.util.DateUtils;
import com.suning.sawp.service.util.ExcelUtils;
import com.suning.sawp.service.util.RedisCacheUtils;
import com.suning.sawp.service.util.UploadCouponFileUtil;
import com.suning.vgs.follow.remote.intf.StoreConsumerService;
import com.suning.vgs.follow.remote.intf.StoreManRemoteService;
import com.suning.vgs.follow.remote.store.ConsumerBehaviorDto;
import com.suning.vgs.follow.remote.store.ConsumerBehaviorType;

@Service
public class HorizPromotionServiceImpl implements HorizPromotionService {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerConstants.SAWP_INTEGRATION);

    /**
     * 异业券随机生成序列key
     */
    private static final String COUPON_HEAD_SEQ_KEY = "coupon_head";

    /**
     * 异业券随机序列号后6位
     */
    private static final int COUPON_SUFFIX_DIGIT = 6;

    /**
     * 异业券随机序列号前4位
     */
    private static final int COUPON_PREFIX_DIGIT = 4;

    @Resource
    MongoTemplate mongoTemplate;

    @Resource(name = "safeMongoTemplate")
    MongoTemplate safeMongoTemplate;

    @Autowired
    MemberInfoService memberInfoService;

    @Autowired
    StoreInfoService storeInfoService;

    @Resource
    PromotionCouponService promotionCouponService;

    /**
     * follow 保存用户发展用户信息接口
     */
    @Resource
    StoreManRemoteService storeManRemoteService;

    @Resource
    PromotionaMgmtService promotionaMgmtService;

    @Resource
    HorizPromotionDaoService horizPromotionDaoService;

    /**
     * 序列服务
     */
    @Autowired
    SequenceSerivce sequenceSerivce;

    /**
     * 
     */
    @Resource(name = "couponFileUtil")
    UploadCouponFileUtil couponFileUtil;

    @Resource
    ShopFrequencyService shopFrequencyService;

    /**
     * 缓存工具
     */
    @Autowired
    RedisCacheUtils cacheUtils;

    @Autowired
    StoreConsumerService storeConsumerService;

    private final SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd");

    @Override
    public ReturnMsg<HorizProRtn> queryHorizProActName(String horizId) {
        ReturnMsg<HorizProRtn> msg = new ReturnMsg<HorizProRtn>();
        HorizProRtn horizRtn = new HorizProRtn();
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and(HorizProCoupon.Attributes.HORIZID).is(horizId);
        query.addCriteria(criteria);
        HorizProCoupon horizAct = mongoTemplate.findOne(query, HorizProCoupon.class);
        if (null != horizAct) {
            if (StringUtils.isNotBlank(horizAct.getCustNum())) {
                // 会员编码不为空，说明此异业券号已被使用
                msg.setError(PromotionErrorConstants.COUPON_SEND_ERR_E005,
                        PromotionErrorConstants.COUPON_SEND_ERR_MAP.get(PromotionErrorConstants.COUPON_SEND_ERR_E005),
                        DJStringUtils.desensitiseMobile(horizAct.getMobile()));
            } else {
                String activityName = horizAct.getProActivityName();
                if (StringUtils.isNotBlank(activityName)) {
                    horizRtn.setActivityName(activityName);
                } else {
                    horizRtn.setActivityName("");
                }
            }
        } else {
            // 根据异业券号未能查到券信息
            msg.setError(PromotionErrorConstants.COUPON_SEND_ERR_E006,
                    PromotionErrorConstants.COUPON_SEND_ERR_MAP.get(PromotionErrorConstants.COUPON_SEND_ERR_E006));
        }
        msg.setData(horizRtn);
        return msg;
    }

    @Override
    public ReturnMsg<String> validateHorizCoupon(String mobile, String horizId, String staffId) {
        ReturnMsg<String> rtn = new ReturnMsg<String>();

        // 查缓存判断当日失败次数是否超过10次
        Integer failedTimes = getFailedTimes(staffId);
        Integer failLimit = Integer.valueOf(ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.FAIL_VALID_HORIZ_COUPON_TIMES, ConfigConstants.DEFAULT_FAIL_VALID_HORIZ_COUPON_TIMES));
        if (failedTimes > failLimit) {
            // 超过限定,直接返回
            rtn.setRetFlag(ReturnMsg.FAIL);
            rtn.setErrorCode(PromotionErrorConstants.VALID_ACT_ERR_E003);
            rtn.setErrorMessage(
                    PromotionErrorConstants.VALID_ACT_ERR_MAP.get(PromotionErrorConstants.VALID_ACT_ERR_E003));
            return rtn;
        }

        LocateMemberInfo memberInfo = memberInfoService.locateMemberByMobileNo(mobile);
        String custNum;

        // 1、判断该手机号是否为会员
        if (null != memberInfo && StringUtils.isNotBlank(memberInfo.getCustNum())) {
            custNum = memberInfo.getCustNum();
        } else {
            rtn.setRetFlag(ReturnMsg.FAIL);
            rtn.setErrorCode(PromotionErrorConstants.COUPON_SEND_ERR_E002);
            rtn.setErrorMessage(
                    PromotionErrorConstants.COUPON_SEND_ERR_MAP.get(PromotionErrorConstants.COUPON_SEND_ERR_E002));
            return rtn;
        }

        // 2、判断券号是否已经被使用
        HorizProCoupon horizCoupon = horizPromotionDaoService.queryHorizCouponByHorId(horizId);
        if (null != horizCoupon) {
            String horCustNum = horizCoupon.getCustNum();
            String hotMobile = horizCoupon.getMobile();
            Date horValCouoTime = horizCoupon.getValiCouponTime();
            if (StringUtils.isNotBlank(horCustNum) && StringUtils.isNotBlank(hotMobile) && null != horValCouoTime) {
                rtn.setRetFlag(ReturnMsg.FAIL);
                rtn.setErrorCode(PromotionErrorConstants.COUPON_SEND_ERR_E005);
                rtn.setErrorMessage(
                        PromotionErrorConstants.COUPON_SEND_ERR_MAP.get(PromotionErrorConstants.COUPON_SEND_ERR_E005));
                return rtn;
            }

            // 3、判断活动是否启用
            HorizProActivity horizProActivity = horizPromotionDaoService
                    .findHorizProActivityByActivityId(horizCoupon.getProActivityId());
            if (HorizProActivity.IsUsed.NO_USED.equals(horizProActivity.getIsUsed())) {
                // 活动已经停用
                rtn.setError(PromotionErrorConstants.COUPON_SEND_ERR_E007,
                        PromotionErrorConstants.COUPON_SEND_ERR_MAP.get(PromotionErrorConstants.COUPON_SEND_ERR_E007));
            } else {
                // 调用促销中心发券
                SendCouponRtn couponRtn = promotionCouponService.sendCoupon(horizCoupon.getProActivityId(), "",
                        memberInfo.getCustNum(), horizCoupon.getActivitySecretKey(),
                        PromSysConstants.BONUS_HORIZ_PROMOTION, null, "");
                // 判断发券返回结果
                if ((couponRtn != null) && (PromotionConstants.SUCCESS.equals(couponRtn.getRetFlag()))) {
                    rtn.setRetFlag(ReturnMsg.SUCCESS);
                    String storeCode = "";
                    String storeName = "";
                    String regionCode = "";
                    String regionName = "";

                    if (StringUtils.isNotBlank(staffId)) {
                        storeCode = storeManRemoteService.getStoreCodeByStaffId(staffId);
                        if (StringUtils.isNotEmpty(storeCode) && !("null".equals(storeCode))) {
                            StoreInfo storeInfo = storeInfoService.queryStoreByCode(storeCode);
                            storeName = storeInfo.getStoreName();
                            regionCode = storeInfo.getRegionCode();
                            regionName = storeInfo.getRegionName();
                        }
                    }
                    // 更新异业优惠券记录表
                    HorizProCouponParam couponParam = new HorizProCouponParam();
                    couponParam.setHorizId(horizId);
                    couponParam.setCustNum(custNum);
                    couponParam.setMobile(mobile);
                    couponParam.setStaffId(staffId);
                    couponParam.setRegionCode(regionCode);
                    couponParam.setRegionName(regionName);
                    couponParam.setStoreCode(storeCode);
                    couponParam.setStoreName(storeName);
                    horizPromotionDaoService.updateHorizProCoupon(couponParam);

                    // 更新促销活动记录表 - 已发放券
                    horizPromotionDaoService.incrActivityHasSend(horizProActivity.getProActivityId());

                    // 会员消费行为采集
                    ConsumerBehaviorDto conDto = new ConsumerBehaviorDto();
                    conDto.setCustNo(custNum);
                    conDto.setStaffId(staffId);
                    conDto.setPhoneNo(mobile);
                    conDto.setBehaviorType(ConsumerBehaviorType.HORIZCOUPON);
                    conDto.setBehaviorTime(System.currentTimeMillis());
                    conDto.setBehaviorName(horizProActivity.getActivityName());
                    storeConsumerService.upsertConsumerBehavior(conDto);
                } else if (couponRtn != null) {
                    // 返回客户端 接口返回的错误编码和错误信息 服务端不做封装 产品确认
                    rtn.setRetFlag(ReturnMsg.FAIL);
                    rtn.setErrorCode(couponRtn.getErrorCode());
                    rtn.setErrorMessage(couponRtn.getErrorMsg());
                } else {
                    // 发券接口返回对象为空，处理失败
                    rtn.setRetFlag(ReturnMsg.FAIL);
                    rtn.setErrorCode(PromotionErrorConstants.COUPON_SEND_ERR_E004);
                    rtn.setErrorMessage(PromotionErrorConstants.COUPON_SEND_ERR_MAP
                            .get(PromotionErrorConstants.COUPON_SEND_ERR_E004));
                }
            }
        } else {
            // 失败更新缓存，失败次数+1
            cacheUtils.hset(CacheKeyConstants.VALID_HORIZCOUPON_FAIL_TIMES, sd.format(new Date()) + staffId,
                    failedTimes + 1);
            // 券号不存在
            rtn.setError(PromotionErrorConstants.COUPON_SEND_ERR_E006,
                    PromotionErrorConstants.COUPON_SEND_ERR_MAP.get(PromotionErrorConstants.COUPON_SEND_ERR_E006));
        }

        return rtn;
    }

    public QueryResult<ValidHorizProCouponRecord> queryHorizProCouponByStaffId(String staffId, int startIndex,
            int limit) {
        LOGGER.debug("HorizPromotionService queryHorizProCouponByStaffId invoke, staffId:{}", staffId);
        // 查询符合条件的总记录数
        int totalSize = horizPromotionDaoService.countHorizProCouponByStaffId(staffId);
        // 查询分页数据
        List<HorizProCoupon> list = horizPromotionDaoService.findHorizProCouponsByStaffId(staffId, startIndex, limit);
        // 会员编码列表
        List<String> custList = new ArrayList<String>();

        List<ValidHorizProCouponRecord> records = new ArrayList<ValidHorizProCouponRecord>();
        if (CollectionUtils.isNotEmpty(list)) {
            for (HorizProCoupon coupon : list) {
                ValidHorizProCouponRecord couponRecord = new ValidHorizProCouponRecord();
                custList.add(coupon.getCustNum());
                couponRecord.setHorizId(coupon.getHorizId());
                // 手机号加密（手机打电话用）
                couponRecord.setMobile(AESUtils.encrypt(coupon.getMobile(), ConfigConstants.AES_KEY));
                couponRecord.setCustNo(coupon.getCustNum());
                couponRecord.setProActivityName(coupon.getProActivityName());
                couponRecord.setValidTime(
                        DateUtils.getDateStrByFormat(coupon.getValiCouponTime(), DateUtils.ACCURATE_MINUTES_PATTERN));
                // 从zookeeper上取会员头像url
                // 会员画像url
                String memberProtraitUrl = DJStringUtils.getMemberProtraitUrl(coupon.getCustNum());
                couponRecord.setPortraitUrl(memberProtraitUrl);
                records.add(couponRecord);
            }
            // 批量更新首复购状态
            batchUpdateDealType(records, custList);
        }

        return new QueryResult<ValidHorizProCouponRecord>(totalSize, records);
    }

    /**
     * 批量更新首复购标记
     * 
     * @param records
     * @param custList
     */
    private void batchUpdateDealType(List<ValidHorizProCouponRecord> records, List<String> custList) {
        Map<String, String> frequencyList = shopFrequencyService.queryShopFrequency(custList);
        for (ValidHorizProCouponRecord couponRecord : records) {
            String frequency = frequencyList.get(couponRecord.getCustNo());
            CustshopFrequency custshopFrequency = DJStringUtils.getDealType(frequency);
            couponRecord.setDealTypeTxt(custshopFrequency.getDealTypeTxt());
            couponRecord.setDealType(custshopFrequency.getDealType());
            // 会员加密
            couponRecord.setCustNo(AESUtils.encrypt(couponRecord.getCustNo(), ConfigConstants.AES_KEY));
        }
    }

    @Override
    public List<HorizProActivityShowDto> queryAvailableHorizActivity() {
        List<HorizProActivityShowDto> result = new ArrayList<HorizProActivityShowDto>();
        // 查询促销活动列表
        QryActDetailRsp rsp = promotionCouponService.queryProActivityList("", PromSysConstants.BONUS_HORIZ_PROMOTION,
                null, null);
        // 依次查询各个促销活动的券剩余量
        if (null != rsp && CollectionUtils.isNotEmpty(rsp.getActivityList())) {
            List<ActivityDetail> list = rsp.getActivityList();
            for (ActivityDetail detail : list) {
                // 赋值到促销活动展示用的dto中
                HorizProActivityShowDto dto = new HorizProActivityShowDto();
                dto.setActivitySecretKey(detail.getActivitySecretKey());
                dto.setEndTime(detail.getEndTime());
                dto.setProActivityId(detail.getActivityId());
                dto.setProActivityName(detail.getActivityName());
                dto.setStartTime(detail.getStartTime());

                QryPromExecDetailReq req = new QryPromExecDetailReq();
                req.setActivityId(detail.getActivityId());
                try {
                    QryPromExecDetailRsp resp = promotionaMgmtService.queryPromotionExecDetail(req);
                    dto.setActRemainCount(resp.getActRemainCount());
                } catch (RscException e) {
                    LOGGER.error("promotionaMgmtService queryPromotionExecDetail error.", e);
                }

                result.add(dto);
            }
        }

        return result;
    }

    @Override
    public ReturnMsg<QueryResult<HorizProRecordShowDto>> queryHorizProRecords(String activityId, String staffId,
            int startIndex, int limit) {
        ReturnMsg<QueryResult<HorizProRecordShowDto>> returnMsg = new ReturnMsg<QueryResult<HorizProRecordShowDto>>();
        List<HorizProRecordShowDto> list = new ArrayList<HorizProRecordShowDto>();
        try {
            int totalSize = horizPromotionDaoService.countHorizProRecord(activityId, staffId);
            List<HorizProRecord> records = horizPromotionDaoService.findHorizProRecords(activityId, staffId, startIndex,
                    limit);
            if (CollectionUtils.isNotEmpty(records)) {
                for (HorizProRecord record : records) {
                    HorizProRecordShowDto dto = new HorizProRecordShowDto();
                    dto.setActivityId(record.getActivityId());
                    dto.setFileName(record.getFileName());
                    dto.setImportBatch(record.getImportBatch());
                    dto.setOperateTime(
                            DateUtils.getDateStrByFormat(record.getOperateTime(), DateUtils.DEFAULT_TIME_PATTERN));
                    dto.setOperator(record.getOperator());
                    dto.setImportNum(record.getImportNum());
                    list.add(dto);
                }
            }

            returnMsg.setData(new QueryResult<HorizProRecordShowDto>(totalSize, list));
        } catch (Exception e) {
            LOGGER.error("horizPromotionDaoService.findHorizProRecords error.", e);
            returnMsg.setError(ErrorCodeConstants.ERR_0002);
        }

        return returnMsg;
    }

    @Override
    public ReturnMsg<String> delHorizProCouponByBatchCode(String importBatchCode, String activityId, String staffId) {
        ReturnMsg<String> returnMsg = new ReturnMsg<String>();
        try {
            // 根据批次号查询该批次导入券总量
            int total = horizPromotionDaoService.deleteHorizCouponByimpBatchCd(importBatchCode);
            // 促销活动记录实际券总量对应减少total
            horizPromotionDaoService.updateActivityActualActCount(staffId, activityId, -total);
            // 删除此条导入记录
            horizPromotionDaoService.deleteHorizRecordByImportBatch(importBatchCode);
        } catch (Exception e) {
            LOGGER.error("HorizPromotionService.delHorizProCouponByBatchCode error, importBatchCode:" + importBatchCode,
                    e);
            returnMsg.setError(ErrorCodeConstants.ERR_0002);
        }

        return returnMsg;
    }

    @Override
    public ReturnMsg<QueryResult<QueryHorActBean>> queryHorizActivity(String activityName, String proActivityId,
            String startTimeStr, String endTimeStr, int currIndex, int pageSize, String staffId) {
        ReturnMsg<QueryResult<QueryHorActBean>> ret = new ReturnMsg<QueryResult<QueryHorActBean>>();
        // 获取最大分页查询数量 如果超过最大默认值，取默认值
        Integer queryLimit = Integer.valueOf(ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.COUPON_RECORD_QUERY_LIMIT, ConfigConstants.DEFAULT_QUERY_COUPON_LIMIT));
        int newPageSize = pageSize > queryLimit ? queryLimit : pageSize;

        // 如果是第一页数据请求，同步促销中心活动数据, 新增券活动
        if (0 == currIndex) {
            syncPromotionActivity(staffId);
        }
        Date startTime = null;
        Date endTime = null;
        if (StringUtils.isNotBlank(startTimeStr)) {
            startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_TIME_PATTERN);
        }
        if (StringUtils.isNotBlank(endTimeStr)) {
            endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_TIME_PATTERN);
        }
        // 符合条件的活动总记录
        int totalSize = horizPromotionDaoService.countHorizProActivity(activityName, proActivityId, startTime, endTime);
        // 分页查询促销活动列表
        List<HorizProActivity> horiActList = horizPromotionDaoService.findActivity4Page(activityName, proActivityId,
                startTime, endTime, currIndex, newPageSize);
        // 组装促销活动返回字段
        List<QueryHorActBean> queryHorActList = new ArrayList<QueryHorActBean>();
        for (HorizProActivity horActivity : horiActList) {
            QueryHorActBean queryHorAct = new QueryHorActBean();
            queryHorAct.setActivityName(horActivity.getActivityName());
            queryHorAct.setProActivityId(horActivity.getProActivityId());
            queryHorAct.setProActivityName(horActivity.getProActivityName());
            queryHorAct.setActivitySecretKey(horActivity.getActivitySecretKey());
            queryHorAct.setCreator(horActivity.getCreator());
            queryHorAct.setStartTime(
                    DateUtils.getDateStrByFormat(horActivity.getStartTime(), DateUtils.DEFAULT_TIME_PATTERN));
            queryHorAct
                    .setEndTime(DateUtils.getDateStrByFormat(horActivity.getEndTime(), DateUtils.DEFAULT_TIME_PATTERN));
            // 已发放券数量
            queryHorAct.setHasSend(horActivity.getHasSend());
            queryHorAct.setIsUsed(horActivity.getIsUsed());

            // 实际生成券总量
            queryHorAct.setActCount(horActivity.getActualActCount());
            // 剩余券(促销理论券上限)
            queryHorAct.setRemainActCount(horActivity.getActCount());
            // 促销(esb查询) - 实际券总量的差值
            queryHorAct.setdValue(horActivity.getActCount() - horActivity.getActualActCount());
            // 生成券文件地址
            queryHorAct.setCouponFileUrl(horActivity.getCouponFileUrl());
            // 处理操作状态
            // 说明生成券成功
            if (null != horActivity.getActualActCount() && horActivity.getActualActCount() > 0) {
                if (StringUtils.isNotBlank(horActivity.getCouponFileUrl())) {
                    queryHorAct.setStatus(OperStatus.CREATE);
                } else {
                    queryHorAct.setStatus(OperStatus.IMPORT);
                }
            } else {
                // 如果生成券的大小是0,或者null,判断是否是正在生成券操作
                // 去缓存中状态, 如果存在
                String statusType = cacheUtils.hget(HorizProCoupon.Status.CACHE_NAME, horActivity.getProActivityId(),
                        String.class);
                // 正在生成券操作中
                if (HorizProCoupon.Status.COUPON_BEGIN.equals(statusType)) {
                    queryHorAct.setStatus(OperStatus.CREATEING);
                } else if (HorizProCoupon.Status.COUPON_FILE_FAIL.equals(statusType)) {
                    // 该活动正在生成券文件,待导出文件
                    queryHorAct.setStatus(OperStatus.CREATE_FAULE);
                } else {
                    queryHorAct.setStatus(OperStatus.DEFAULT);
                }
            }
            queryHorActList.add(queryHorAct);
        }
        QueryResult<QueryHorActBean> queryResult = new QueryResult<QueryHorActBean>(totalSize, queryHorActList);
        ret.setData(queryResult);
        return ret;
    }

    /**
     * 
     * 功能描述: 同步促销中心活动<br>
     * 〈功能详细描述〉
     *
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private void syncPromotionActivity(String staffId) {
        // 查询ESB当前可用促销活动列表
        List<HorizProActivityShowDto> dtos = queryAvailableHorizActivity();
        if (CollectionUtils.isNotEmpty(dtos)) {
            // 如果促销列表不为空，检查促销活动列表是否已经包含此活动，如果没有则新增
            for (HorizProActivityShowDto showDto : dtos) {
                HorizProActivity activity = horizPromotionDaoService
                        .findHorizProActivityByActivityId(showDto.getProActivityId());
                if (null == activity) {
                    // 此类活动尚未存在于活动列表中
                    addNewHorizProActivity(showDto, staffId);
                }
            }
        }
    }

    /**
     * 
     * 功能描述: 新增促销活动<br>
     * 〈功能详细描述〉
     *
     * @param showDto
     * @param staffId
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private void addNewHorizProActivity(HorizProActivityShowDto showDto, String staffId) {
        HorizProActivity horizActivity = new HorizProActivity();
        horizActivity.setActivityName(showDto.getProActivityName());
        horizActivity.setProActivityId(showDto.getProActivityId());
        horizActivity.setProActivityName(showDto.getProActivityName());
        horizActivity.setActivitySecretKey(showDto.getActivitySecretKey());
        horizActivity.setCreator(staffId);
        horizActivity.setCreateTime(new Date());
        horizActivity.setStartTime(DateUtils.getDateByStr(showDto.getStartTime(), DateUtils.DEFAULT_TIME_PATTERN));
        horizActivity.setEndTime(DateUtils.getDateByStr(showDto.getEndTime(), DateUtils.DEFAULT_TIME_PATTERN));
        // 促销中心券总量
        horizActivity.setActCount(Integer.valueOf(showDto.getActRemainCount()));
        horizPromotionDaoService.addHorizProActivity(horizActivity);
    }

    @Override
    public ReturnMsg<String> updateActivityStatus(String activityId, String optCode, String staffId) {
        ReturnMsg<String> returnMsg = new ReturnMsg<String>();
        try {
            // 判断操作码是否合法
            if (HorizProActivity.IsUsed.USED.equals(optCode) || HorizProActivity.IsUsed.NO_USED.equals(optCode)) {
                horizPromotionDaoService.updateActivityStatus(activityId, optCode, staffId);
            } else {
                // 非法操作码
                returnMsg.setError(ErrorCodeConstants.ERR_1013);
            }
        } catch (Exception e) {
            LOGGER.error("horizPromotionDaoService.updateActivityStatus ERROR.", e);
            returnMsg.setError(ErrorCodeConstants.ERR_0002);
        }
        return returnMsg;
    }

    @Override
    public ReturnMsg<String> checkHorizProCouponCount(String startTime, String endTime) {
        ReturnMsg<String> returnMsg = new ReturnMsg<String>();
        if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) {
            returnMsg.setError(ErrorCodeConstants.ERR_0002);
        } else {
            Date startDate = DateUtils.getDateByStr(startTime, DateUtils.DEFAULT_DATE_PATTERN);
            Date endDate = DateUtils.getDateByStr(endTime, DateUtils.DEFAULT_DATE_PATTERN);
            int totalSize = horizPromotionDaoService.countValidHorizCoupon(startDate, endDate);
            String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH, ConfigConstants.MAX_EXPORT_COUNT,
                    ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
            if (totalSize > Integer.parseInt(maxCount)) {
                returnMsg.setError(ErrorCodeConstants.ERR_0006, maxCount);
            }
        }

        return returnMsg;
    }

    @Override
    public Workbook exportValidHorizCoupon(String startTime, String endTime) {
        Workbook workbook = null;
        try {
            if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
                Date startDate = DateUtils.getDateByStr(startTime, DateUtils.DEFAULT_DATE_PATTERN);
                Date endDate = DateUtils.getDateByStr(endTime, DateUtils.DEFAULT_DATE_PATTERN);

                int totalSize = horizPromotionDaoService.countValidHorizCoupon(startDate, endDate);
                String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                        ConfigConstants.MAX_EXPORT_COUNT, ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
                if (totalSize <= Integer.parseInt(maxCount)) {
                    List<HorizProCoupon> coupons = horizPromotionDaoService.findValidHorizProCoupons(startDate,
                            endDate);
                    // 生成EXCEL
                    // 带生成的标题栏
                    String[] titles = { "活动名称", "大区名称", "门店名称", "券号", "工号", "验券时间" };
                    // 准备待导出的数据
                    List<String[]> datas = new ArrayList<String[]>();
                    for (HorizProCoupon coupon : coupons) {
                        String[] data = new String[6];
                        data[0] = coupon.getProActivityName();
                        data[1] = coupon.getRegionName();
                        data[2] = coupon.getStoreName();
                        data[3] = coupon.getHorizId();
                        data[4] = coupon.getStaffId();
                        data[5] = DateUtils.getDateStrByFormat(coupon.getValiCouponTime(),
                                DateUtils.DEFAULT_TIME_PATTERN);
                        datas.add(data);
                    }

                    // 导出EXCEL
                    workbook = ExcelUtils.createExcel(datas, titles);
                }
            }
        } catch (Exception e) {
            LOGGER.error("HorizPromotionService.exportValidHorizCoupon error.", e);
        }

        return workbook;
    }

    /**
     * 根据活动ID查询活动信息
     * 
     * @param activityId
     * @return
     */
    @Override
    public HorizProActivity queryHorizProActivityByActivityId(String activityId) {
        return horizPromotionDaoService.findHorizProActivityByActivityId(activityId);
    }

    /**
     * 批量生成异业券
     * 
     * @throws Exception
     */
    @Override
    public void batchCreateDifferentIndustryCertificate(String activityId, Integer count) {
        // 查询活动详情,
        HorizProActivity activity = horizPromotionDaoService.findHorizProActivityByActivityId(activityId);
        // 判断详情是否存在
        if (null == activity) {
            // 活动不存在
            throw new MobileException(ErrorCodeConstants.ERR_1010);
        } else {
            // 判断活动状态是否是启用
            if (HorizProActivity.IsUsed.NO_USED.equals(activity.getIsUsed())) {
                throw new MobileException(ErrorCodeConstants.ERR_0010);
            } else if (null != activity.getActualActCount() && activity.getActualActCount() > 0) {
                // 判断活动是否已经生成过券，包含手动生成，或者导入生成
                throw new MobileException(ErrorCodeConstants.ERR_0011);
            } else {
                // 去缓存中状态, 如果存在
                String statusType = cacheUtils.hget(HorizProCoupon.Status.CACHE_NAME, activityId, String.class);
                if (HorizProCoupon.Status.COUPON_BEGIN.equals(statusType)) {
                    // 该活动正在生成券
                    throw new MobileException(ErrorCodeConstants.ERR_0012);
                } else if (HorizProCoupon.Status.COUPON_FILE_BEGIN.equals(statusType)) {
                    // 该活动正在生成券文件,待导出文件
                    throw new MobileException(ErrorCodeConstants.ERR_0013);
                } else if (HorizProCoupon.Status.COUPON_FILE_FAIL.equals(statusType)) {
                    // 该活动正在生成券文件,待导出文件
                    throw new MobileException(ErrorCodeConstants.ERR_0015);
                } else {
                    try {
                        // 设置缓存状态为正则生成券
                        cacheUtils.hset(HorizProCoupon.Status.CACHE_NAME, activityId,
                                HorizProCoupon.Status.COUPON_BEGIN);
                        // 如果库中不存在该序列,需要新增
                        sequenceSerivce.addNewSequence(COUPON_HEAD_SEQ_KEY, 0L);
                        // 获取本次新增券前缀,默认是4位数字,不足补0
                        String couponHead = sequenceSerivce.incrSequenceNumber(COUPON_HEAD_SEQ_KEY);
                        if (null == couponHead || couponHead.length() > COUPON_PREFIX_DIGIT) {
                            throw new MobileException(ErrorCodeConstants.ERR_0014);
                        } else {
                            // 转换成固定长度
                            couponHead = convertCouponHead(couponHead);
                            // 计算后6位随机码
                            Set<String> couponList = createCouponNo(count);

                            // 启线程进行文件上传
                            CountDownLatch signal = new CountDownLatch(1);
                            StringBuilder fileUrl = new StringBuilder();
                            // 创建新线程异步处理文件上传
                            new UploadCouponFile(couponList, couponHead, activityId, fileUrl, signal).start();

                            // 转换成迭代器
                            Iterator<String> couponIt = couponList.iterator();
                            // 数据集合
                            List<HorizProCoupon> batchCoupon = new ArrayList<HorizProCoupon>();
                            HorizProCoupon coupon = null;
                            long start = System.currentTimeMillis();
                            // 循环随机码,动态生成数据
                            while (null != couponIt && couponIt.hasNext()) {
                                coupon = new HorizProCoupon();
                                // 券号
                                coupon.setHorizId(couponHead + couponIt.next());
                                // 活动ID
                                coupon.setProActivityId(activity.getProActivityId());
                                // 活动名称
                                coupon.setProActivityName(activity.getProActivityName());
                                // 活动密钥
                                coupon.setActivitySecretKey(activity.getActivitySecretKey());
                                batchCoupon.add(coupon);
                                // 集合大于限定数值,执行批量入库
                                if (batchCoupon.size() >= 5000) {
                                    safeMongoTemplate.insert(batchCoupon, HorizProCoupon.class);
                                    // 执行完清空数据集合
                                    batchCoupon.clear();
                                }
                            }
                            // 循环结束后,如果集合中还存在数据,需要继续插入库中
                            if (!batchCoupon.isEmpty()) {
                                safeMongoTemplate.insert(batchCoupon, HorizProCoupon.class);
                                batchCoupon.clear();
                            }
                            LOGGER.info("save data to mongodb cost " + (System.currentTimeMillis() - start) + " ms");
                            try {
                                // 主线程等待文件上传线程处理完成,同步方法
                                signal.await();
                            } catch (InterruptedException e) {
                                LOGGER.error("couponFileThread error", e);
                            }

                            LOGGER.info(
                                    "save data and upload file cost " + (System.currentTimeMillis() - start) + " ms");
                            // 判断上传地址是否为空
                            if (StringUtils.isBlank(fileUrl.toString())) {
                                // 设置状态为文件生成失败
                                cacheUtils.hset(HorizProCoupon.Status.CACHE_NAME, activityId,
                                        HorizProCoupon.Status.COUPON_FILE_FAIL);
                                // 保存数据,设置默认时间7天
                                cacheUtils.hsetWithMyExpireTime(HorizProCoupon.Status.CACHE_NAME, activityId + "_data",
                                        couponList, CacheKeyConstants.CITY_EXPIRE_TIME);
                                cacheUtils.hsetWithMyExpireTime(HorizProCoupon.Status.CACHE_NAME,
                                        activityId + "_couponHead", couponHead, CacheKeyConstants.CITY_EXPIRE_TIME);
                                // 该活动正在生成券文件,待导出文件
                                throw new MobileException(ErrorCodeConstants.ERR_0015);
                            } else {
                                LOGGER.info("couponFile, " + fileUrl.toString());
                                horizPromotionDaoService.updateActivityCouponFileUrl(activityId, fileUrl.toString(),
                                        count);
                                cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId);
                                cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId + "_data");
                                cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId + "_couponHead");
                            }
                        }
                    } catch (Exception e) {
                        LOGGER.error("batchCreateDifferentIndustryCertificate error", e);
                        String type = cacheUtils.hget(HorizProCoupon.Status.CACHE_NAME, activityId, String.class);
                        // 如果缓存状态为正常生成,说明是在生成中产生的异常,需要清除,以便下面继续进行操作
                        if (HorizProCoupon.Status.COUPON_BEGIN.equals(type)) {
                            // 清除缓存
                            cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId);
                        }
                        throw new RuntimeException();
                    }
                }
            }
        }
    }

    /**
     * 转换成固定长度,前缀补0
     * 
     * @param couponHead
     * @return
     */
    private String convertCouponHead(String couponHead) {
        String head = couponHead;
        if (null != head && head.length() != COUPON_PREFIX_DIGIT) {
            for (int i = 0; i < COUPON_PREFIX_DIGIT; i++) {
                head = "0" + head;
            }
            head = head.substring(head.length() - COUPON_PREFIX_DIGIT);
        }
        return head;
    }

    /**
     * 创建随机券号
     * 
     * @param count
     * @return
     */
    private Set<String> createCouponNo(int count) {
        long start = System.currentTimeMillis();
        // 取得随机码位数,确定该位数最大
        int max = getMax(COUPON_SUFFIX_DIGIT);
        int cnt = count;
        // 如果生成券总数大于最大值,默认赋值
        if (cnt > max) {
            cnt = max;
        }
        // 用Set去重
        Set<String> couponList = new HashSet<String>(cnt);
        // 循环生成,直到集合中满足
        while (couponList.size() < cnt) {
            // 随机生成6位数字字符串
            couponList.add(RandomStringUtils.randomNumeric(COUPON_SUFFIX_DIGIT));
        }
        LOGGER.info("cost " + (System.currentTimeMillis() - start) + " ms");
        return couponList;
    }

    /**
     * 根据位数,取最大值
     * 
     * @param digit
     * @return
     */
    private int getMax(int digit) {
        int cnt = digit;
        String num = "0";
        // 循环位数
        while (cnt > 0) {
            cnt--;
            // 拼接最大值9
            num += "9";
        }
        try {
            // 转换数字
            return Integer.parseInt(num);
        } catch (Exception e) {
            LOGGER.error("getMax parseInt error", e);
            return Integer.MAX_VALUE;
        }
    }

    @Override
    public void retryCreateCouponFile(String activityId) {
        // 查询活动详情,
        HorizProActivity activity = horizPromotionDaoService.findHorizProActivityByActivityId(activityId);
        // 判断详情是否存在
        if (null == activity) {
            // 活动不存在
            throw new MobileException(ErrorCodeConstants.ERR_1010);
        } else {
            // 判断活动状态是否是启用
            if (HorizProActivity.IsUsed.NO_USED.equals(activity.getIsUsed())) {
                throw new MobileException(ErrorCodeConstants.ERR_0010);
            } else if (null != activity.getActualActCount() && activity.getActualActCount() > 0) {
                // 判断活动是否已经生成过券，包含手动生成，或者导入生成
                throw new MobileException(ErrorCodeConstants.ERR_0011);
            } else {
                // 取缓存中状态, 如果存在
                String statusType = cacheUtils.hget(HorizProCoupon.Status.CACHE_NAME, activityId, String.class);
                // 不是生成文件失败状态
                if (!HorizProCoupon.Status.COUPON_FILE_FAIL.equals(statusType)) {
                    throw new MobileException(ErrorCodeConstants.ERR_0016);
                } else {
                    Type type = new TypeToken<Set<String>>() {
                    }.getType();
                    // 缓存中取数据
                    Set<String> couponList = cacheUtils.hget(HorizProCoupon.Status.CACHE_NAME, activityId + "_data",
                            type);
                    String couponHead = cacheUtils.hget(HorizProCoupon.Status.CACHE_NAME, activityId + "_couponHead",
                            String.class);
                    // 数据丢失,暂不处理
                    if (null == couponList || couponList.isEmpty() || StringUtils.isBlank(couponHead)) {
                        throw new MobileException(ErrorCodeConstants.ERR_0017);
                    } else {
                        // 更新缓存状态为生成文件
                        cacheUtils.hset(HorizProCoupon.Status.CACHE_NAME, activityId,
                                HorizProCoupon.Status.COUPON_FILE_BEGIN);
                        // 异步处理生成文件
                        String url = couponFileUtil.createFileAndUpload(couponList, couponHead, activityId);
                        // 判断上传地址是否为空
                        if (StringUtils.isBlank(url)) {
                            // 设置状态为文件生成失败
                            cacheUtils.hset(HorizProCoupon.Status.CACHE_NAME, activityId,
                                    HorizProCoupon.Status.COUPON_FILE_FAIL);
                            // 该活动正在生成券文件,待导出文件
                            throw new MobileException(ErrorCodeConstants.ERR_0015);
                        } else {
                            LOGGER.info("couponFile, " + url);
                            horizPromotionDaoService.updateActivityCouponFileUrl(activityId, url, couponList.size());
                            cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId);
                            cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId + "_data");
                            cacheUtils.clearCacheData(HorizProCoupon.Status.CACHE_NAME, activityId + "_couponHead");
                        }
                    }
                }
            }
        }

    }

    /**
     * 
     * 功能描述:获取当天验证异业券失败次数 <br>
     * 〈功能详细描述〉
     * 
     * @param staffId
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private Integer getFailedTimes(String staffId) {
        Integer failedTimes = cacheUtils.hget(CacheKeyConstants.VALID_HORIZCOUPON_FAIL_TIMES,
                sd.format(new Date()) + staffId, Integer.class);
        return failedTimes == null ? Integer.valueOf("0") : failedTimes;
    }

    @Override
    public ReturnMsg<QueryResult<StatisticsHorCoupon>> statisHorCoupon(String activityName, String startTimeStr,
            String endTimeStr, String regionCode, String storeCode, int startIndex, int limit) {
        ReturnMsg<QueryResult<StatisticsHorCoupon>> ret = new ReturnMsg<QueryResult<StatisticsHorCoupon>>();
        QueryResult<StatisticsHorCoupon> queryRet = new QueryResult<StatisticsHorCoupon>();
        Date startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        Date endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        // 符合条件的活动总记录
        List<StatisticsHorCoupon> list = horizPromotionDaoService.statisHorizCoupon(activityName, regionCode, storeCode,
                startTime, endTime, startIndex, limit);
        int totalSize = list.size();
        queryRet.setTotalSize(totalSize);
        List<StatisticsHorCoupon> retList = new ArrayList<StatisticsHorCoupon>();
        int endIndex = startIndex + limit;
        if (endIndex > totalSize) {
            endIndex = totalSize;
        }
        for (int i = startIndex; i < endIndex; i++) {
            retList.add(list.get(i));
        }
        queryRet.setRecords(retList);
        ret.setData(queryRet);
        return ret;
    }

    /**
     * 异业券生成文件并上传线程
     * 
     * @author 15050519
     *
     */
    public class UploadCouponFile extends Thread {
        private Set<String> couponList;
        private String couponHead;
        private String activityId;
        private StringBuilder fileUrl;
        private CountDownLatch signal;

        public UploadCouponFile() {
        }

        public UploadCouponFile(Set<String> couponList, String couponHead, String activityId, StringBuilder url,
                CountDownLatch signal) {
            this.couponList = couponList;
            this.couponHead = couponHead;
            this.activityId = activityId;
            this.fileUrl = url;
            this.signal = signal;
        }

        @Override
        public void run() {
            try {
                // 生成文件并上传至静态资源服务器,并返回地址url
                String url = couponFileUtil.createFileAndUpload(couponList, couponHead, activityId);
                // 不为空时,作为线程处理返回数据,返回主线程处理
                if (StringUtils.isNotBlank(url) && null != fileUrl) {
                    fileUrl.append(url);
                }
            } catch (Exception e) {
                LOGGER.error("UploadCouponFile thread error, ", e);
            } finally {
                // 处理完成,计数
                if (null != signal) {
                    signal.countDown();
                }
            }
        }
    }

    @Override
    public Workbook exportHorCouponStat(String activityName, String startTimeStr, String endTimeStr, String regionCode,
            String storeCode) {
        Workbook workbook = null;
        try {
            Date startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
            Date endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
            String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH, ConfigConstants.MAX_EXPORT_COUNT,
                    ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
            List<StatisticsHorCoupon> list = horizPromotionDaoService.statisHorizCoupon(activityName, regionCode,
                    storeCode, startTime, endTime, null, null);
            if (list.size() <= Integer.parseInt(maxCount)) {
                // 生成EXCEL
                // 带生成的标题栏
                String[] titles = { "活动名称", "大区名称", "门店名称", "发券数量" };
                // 准备待导出的数据
                List<String[]> datas = new ArrayList<String[]>();
                for (StatisticsHorCoupon coupon : list) {
                    String[] data = new String[4];
                    data[0] = coupon.getActivityName();
                    data[1] = coupon.getRegionName();
                    data[2] = coupon.getStoreName();
                    data[3] = String.valueOf(coupon.getCouponAmount());
                    datas.add(data);
                }
                // 导出EXCEL
                workbook = ExcelUtils.createExcel(datas, titles);
            }
        } catch (Exception e) {
            LOGGER.error("异业券统计导出异常", e);
        }
        return workbook;
    }

    @Override
    public ReturnMsg<String> checkCouponStatExport(String activityName, String startTimeStr, String endTimeStr,
            String regionCode, String storeCode) {
        ReturnMsg<String> returnMsg = new ReturnMsg<String>();
        if (StringUtils.isBlank(startTimeStr) || StringUtils.isBlank(endTimeStr) || StringUtils.isBlank(regionCode)) {
            returnMsg.setError(ErrorCodeConstants.ERR_0001);
            return returnMsg;
        }
        Date startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        Date endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        List<StatisticsHorCoupon> list = horizPromotionDaoService.statisHorizCoupon(activityName, regionCode, storeCode,
                startTime, endTime, null, null);
        String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH, ConfigConstants.MAX_EXPORT_COUNT,
                ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
        Integer exportSize = list == null ? 0 : list.size();
        if (exportSize > Integer.parseInt(maxCount)) {
            returnMsg.setError(ErrorCodeConstants.ERR_0006, maxCount);
        } else if (exportSize == 0) {
            returnMsg.setError(ErrorCodeConstants.ERR_0019);
        } else {
            returnMsg.setData(String.valueOf(list.size()));
        }
        return returnMsg;
    }
}
